package digitrencer;



import java.util.HashMap;
import java.util.Collection;
import java.util.Iterator;

/**
 * A hálózat legfelső szintjét megvalósító osztály.
 * @author taplonaplo
 */
public class Model extends CompositeElement{
        private HashMap<Integer, Output> outputList = new HashMap<Integer, Output>();
        private HashMap<Integer, Switch> switchList = new HashMap<Integer, Switch>();
        private HashMap<Integer, Generator> generatorList = new HashMap<Integer, Generator>();
        private HashMap<Integer, Oscilloscope> oscilloscopeList = new HashMap<Integer, Oscilloscope>();
         /**
         * A Model konstruktora. Nincs felelőssége.
         * @param inputs
         * @param outputs
         */
        public Model(Wire[] inputs, Wire[] outputs){
            super(inputs,outputs);
        }

        /**
         * Metódus új elem hozzáadásához. Az alkatrész által használt vezetékek
         * ID-ja alapján lekérdezi a vezetékekt, majd meghívja a type által
         * definiált elem konstruktorát ezekkel.
         * @param type      Kapu típusa
         * @param inputs    Bementi vezetékek ID alapján
         * @param outputs   Kimeneti vezetékek ID alapján
         * @return Az új egység azonosítója.
         * @throws IllegalArgumentException Ismeretlen típusnév vagy vezeték ID.
         */
        @Override
        public int addElement(String type, int[] inputs, int[] outputs) throws IllegalArgumentException {
            //Vezetékek tömbbe rendezése ID alapján
            Wire[] temp_in = new Wire[inputs.length];
            Wire[] temp_out = new Wire[outputs.length];
            try{
                int j = 0;
                for (int i : inputs)
                    temp_in[j++] = wireList.get(i);
                j = 0;
                for (int o : outputs)
                    temp_out[j++] = wireList.get(o);
            }
            //elkapjuk ha kiindexeltünk valamelyikből
            catch (IndexOutOfBoundsException e){
                throw new IllegalArgumentException("Element requires Wire which does not exist.");
            }

            //Elem hozzáadása
            int id = elementList.size();
            try {
                if (type.compareTo("and") == 0){
                    And_gate newelem = new And_gate(temp_in,temp_out);
                    elementList.add(newelem);
                }
                else if(type.compareTo("or") == 0){
                    Or_gate newelem = new Or_gate(temp_in,temp_out);
                    elementList.add(newelem);
                }
                else if(type.compareTo("inverter") == 0){
                    Inverter newelem = new Inverter(temp_in,temp_out);
                    elementList.add(newelem);
                }
                else if(type.compareTo("led") == 0){
                    Led newelem = new Led(temp_in,temp_out);
                    elementList.add(newelem);
                    outputList.put(id,(Output)newelem);
                }
                else if(type.compareTo("generator") == 0){
                    Generator newelem = new Generator(temp_in,temp_out);
                    elementList.add(newelem);
                    generatorList.put(id, newelem);
                }
                else if(type.compareTo("switch") == 0){
                    Switch newelem = new Switch(temp_in,temp_out);
                    elementList.add(newelem);
                    switchList.put(id, newelem);
                }
                else if(type.compareTo("oscilloscope") == 0){
                    Oscilloscope newelem = new Oscilloscope(temp_in,temp_out);
                    elementList.add(newelem);
                    oscilloscopeList.put(id, newelem);
                    outputList.put(id, newelem);
                }
                else throw new IllegalArgumentException("Unknown element: " + type);
            }
            catch (IllegalArgumentException e){
                throw e;
            }           
            return id;
        }

        /**
         * Metódus egy bementi egység értékének lekérezésére. A megadott ID
         * alapján megprobálja meghívni az Input osztály getValue() metódusát a
         * Switch és Generator listából.
         * @param ID    A bemeneti egység azonosítója.
         * @return A bemeneti egység aktuális értéke.
         * @throws IllegalArgumentException Ha nincs bemeneti elem az adott ID-val.
         */
        public boolean[] getInputValue(int ID) throws IllegalArgumentException{
            if (switchList.get(ID) != null){
                return switchList.get(ID).getValue();
            }
            else if (generatorList.get(ID) != null){
                return generatorList.get(ID).getValue();
            }
            else
                throw new IllegalArgumentException("Element with ID "+ Integer.toString(ID) + " is not an input object.");
        }

        /**
         * Metódus egy kimeneti egység értékének lekérezésére. A megadott ID
         * alapján megprobálja meghívni az Output osztály getValue() metódusát a
         * Output listából.
         * @param ID    A kimeneti egység azonosítója.
         * @return A kimeneti egység aktuális értéke.
         * @throws IllegalArgumentException Ha nincs kimeneti elem az adott ID-val.
         */
        public boolean[] getOutputValue(int ID) throws IllegalArgumentException{
            if (outputList.get(ID) != null){
                return outputList.get(ID).getValue();
            }
            else
                throw new IllegalArgumentException("Element with ID "+ Integer.toString(ID) + " is not an output object.");
        }

        /**
         * Metódus egy oszcilloszkóp jelsorozatának lekérezésére. A megadott ID
         * alapján megprobálja meghívni az Oscilloscope getHistory() metódusát
         * az Oscilloscope listából.
         * @param ID Az oszcilloszkóp azonosítója.
         * @return Az oszcilloszkópban tárolt jelsorozat.
         * @throws IllegalArgumentException Ha nincs oszcilloszkóp az adott ID-val.
         */
        public boolean[] getOscilloscopeHistory(int ID) throws IllegalArgumentException{
            if (oscilloscopeList.get(ID) != null){
                return oscilloscopeList.get(ID).getHistory();
            }
            else
                throw new IllegalArgumentException("Element with ID "+ Integer.toString(ID) + " is not an oscilloscope.");
        }

        /**
         * Metódus jelgenerátorok léptetésére. A generator lista minden elemére
         * meghívja a léptető függvényt.
         */
        public void stepGenerators() {
            Collection<Generator> c = generatorList.values();
            Iterator<Generator> iter = c.iterator();
            while (iter.hasNext()){
                iter.next().step();
            }
        }

        /**
         * Metódús kapcsoló váltására. A kért kapcsolót az ellenkező állapotába
         * kapcsolja.
         * @param ID A kapcsoló azonosítója.
         * @throws IllegalArgumentException Ha nincs kapcsoló az adott ID-val.
         */
        public void setSwitch(int ID) throws IllegalArgumentException{
             if (switchList.get(ID) != null){
                switchList.get(ID).setSwitch();
            }
            else
                throw new IllegalArgumentException("Element with ID "+ Integer.toString(ID) + " is not a switch.");
        }

        /**
         * Metódus jelgenerátor jelsorozatának módosítására. Meghívja a kapott
         * ID alapján a megfelelő jelgenerátor setSeq() metódusát.
         * @param ID    A jelgenerátor azonosítója.
         * @param seq   A beállítandó jelsorozat.
         * @throws IllegalArgumentException Ha nincs jelgenerátor az adott ID-val.
         */
        public void setSequence(int ID, String seq) throws IllegalArgumentException{
             if (generatorList.get(ID) != null){
                generatorList.get(ID).setSeq(seq);
            }
            else
                throw new IllegalArgumentException("Element with ID "+ Integer.toString(ID) + " is not a generator.");
        }

        /**
         * Metódus jelgenerátor jelsorozatanák lekérdezésére.
         * @param ID A jelgenerátor azonoítója.
         * @return A jelgnerátor jelsorozata.
         * @throws IllegalArgumentException Ha nincs jelgenerátor az adott ID-val.
         */
        public String getSequence(int ID) throws IllegalArgumentException{
            if (generatorList.get(ID) != null){
                return generatorList.get(ID).getSeq();
            }
            else
                throw new IllegalArgumentException("Element with ID "+ Integer.toString(ID) + " is not a generator.");
        }

        /**
         * Metódus a hálózat leállításához. Először az alkatrészek, majd a
         * vezeték értékét állítja alaphelyzetbe, majd a jelgenerátorokat
         * visszaállítja az elejére és törli az oszcilloszkópokban rögzített
         * jelsorozatot.
         */
        @Override
        public void reset() {
            super.reset();

            //Generátorok resetelése
            Collection<Generator> c1 = generatorList.values();
            Iterator<Generator> iter1 = c1.iterator();
            while (iter1.hasNext()){
                iter1.next().reset();
            }
            //Outputok resetelése (futtatjuk őket
            Collection<Output> c2 = outputList.values();
            Iterator<Output> iter2 = c2.iterator();
            while (iter2.hasNext()){
                iter2.next().run();
            }
            //Oszcilloszkopok resetelése
            Collection<Oscilloscope> c3 = oscilloscopeList.values();
            Iterator<Oscilloscope> iter3 = c3.iterator();
            while (iter3.hasNext()){
                iter3.next().reset();
            }
        }

        /**
         * A Model osztály futási metódusa. Az elemeket és vezetékeket felváltva
         * fríssíti, amíg a hálózat nem stabilizálódik, vagy a probálkozások
         * száma túllépi a küszöbértéket. Utána menti a jelet az oszcilloszkópba
         * és lépteti a generátorokat.
         * @throws UnstableNetworkException Ha instabil a hálózat.
         */
        @Override
        public void run() throws UnstableNetworkException {
            super.run();
            //Oszcilloszkópba mentjük a jelet itt már stabil
            Collection<Oscilloscope> c = oscilloscopeList.values();
            Iterator<Oscilloscope> iter = c.iterator();
            while (iter.hasNext()){
                iter.next().register();
            }
        }
}